package com.xzakota.minesweeper;

import com.xzakota.minesweeper.db.RankDaoImpl;
import com.xzakota.minesweeper.db.UserDaoImpl;
import com.xzakota.minesweeper.log.XLogger;
import com.xzakota.minesweeper.model.entity.RoomBean;
import com.xzakota.minesweeper.model.entity.UserBean;
import com.xzakota.minesweeper.msg.*;
import com.xzakota.minesweeper.util.ArrayUtils;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;

/**
 * 已连接客户端的处理器
 *
 * @author Xzakota
 */
public class ConnectedUserHandler extends SocketHandler {
    // 当前登录用户
    public UserBean currentUser;
    // 用户登录监听器
    public UserSignInListener userSignInListener;
    // 用户登出监听器
    public UserSignOutListener userSignOutListener;

    public ConnectedUserHandler(Socket socket) {
        super(socket);
    }

    @Override
    public void run() {
        super.run();

        try {
            while (!socket.isClosed() && !Thread.currentThread().isInterrupted()) {
                ObjectInputStream stream = new ObjectInputStream(reader);
                Object msgObj = stream.readObject();
                if (msgObj instanceof BaseMsg) {
                    XLogger.info("服务端收到信息: " + msgObj);
                    if (msgObj instanceof UserMsg userMsg) {
                        currentUser = userMsg.getUser();

                        if (msgObj instanceof SignInMsg msg) {
                            msg.code = UserDaoImpl.signIn(currentUser);
                            if (msg.code == MsgCode.BASE_SUCCESS) {
                                msg.data = UserDaoImpl.findUser(currentUser.username, false);
                                if (userSignInListener != null) {
                                    userSignInListener.onUserSignIn();
                                }
                            }
                        } else if (msgObj instanceof SignUpMsg msg) {
                            msg.code = UserDaoImpl.signUp(currentUser);
                        } else if (msgObj instanceof SignOutMsg msg) {
                            msg.code = UserDaoImpl.signOut(currentUser);
                            if (userSignOutListener != null && currentUser != null) {
                                userSignOutListener.onUserSignOut();
                            }
                            currentUser = null;
                        }

                        sendMsg(userMsg);
                    } else if (msgObj instanceof GameAllRoomMsg) {
                        GameAllRoomMsg gameRoomsMsg = makeGameRoomsMsg();
                        gameRoomsMsg.code = MsgCode.BASE_SUCCESS;
                        sendMsg(gameRoomsMsg);
                    } else if (msgObj instanceof RoomMsg roomMsg) {
                        int roomNum = roomMsg.getRoomNum();
                        Object[] oldData = (Object[]) roomMsg.data;

                        if (msgObj instanceof UserEnterRoomMsg || msgObj instanceof UserExitRoomMsg) {
                            RoomAtServer room = Server.ROOM.get(roomNum);
                            if (msgObj instanceof UserEnterRoomMsg) {
                                roomMsg.code = room.addUser(this);
                            } else {
                                roomMsg.code = room.removeUser(this);
                            }
                            sendMsg(roomMsg);

                            GameAllRoomMsg gameRoomsMsg = makeGameRoomsMsg();
                            sendMsgToAll(gameRoomsMsg);
                        } else if (msgObj instanceof RoomRankMsg roomRankMsg) {
                            roomRankMsg.code = MsgCode.BASE_SUCCESS;
                            roomRankMsg.data = ArrayUtils.concatenate(
                                    oldData, RankDaoImpl.findUsers(roomNum, 10).toArray()
                            );
                            sendMsg(roomRankMsg);
                        } else if (msgObj instanceof GameWinMsg gameWinMsg) {
                            int time = gameWinMsg.getTime();
                            RoomAtServer room = Server.ROOM.get(roomNum);

                            RoomRankMsg sendMsg = new RoomRankMsg();
                            sendMsg.code = RankDaoImpl.updateTime(currentUser.username, roomNum, time);
                            sendMsg.data = ArrayUtils.concatenate(
                                    new Object[] {oldData[0], oldData[1]},
                                    RankDaoImpl.findUsers(roomNum, 10).toArray()
                            );
                            room.sendMsgToAll(sendMsg);
                        }
                    }
                }
            }
        } catch (IOException e) {
            XLogger.error("客户端 Socket 连接意外中断");
            throw new RuntimeException(e);
        } catch (Exception e) {
            throw new RuntimeException(e);
        } finally {
            close();
            closeStream();
            Server.USER_SET.remove(this);
            XLogger.info("与客户端的 Socket 连接关闭: " + socket.getInetAddress().getHostAddress());
        }
    }

    /**
     * 封装包含在线人数、所有房间状态的消息
     */
    private GameAllRoomMsg makeGameRoomsMsg() {
        GameAllRoomMsg msg = new GameAllRoomMsg();
        msg.code = MsgCode.BASE_SUCCESS;
        Map<Integer, RoomBean> map = new HashMap<>();
        Server.ROOM.forEach((k, v) -> {
            RoomBean room = new RoomBean(k);
            room.roomCurrentOnline = v.roomCurrentOnline;
            room.roomLevel = v.roomLevel;
            map.put(k, room);
        });
        msg.data = map;
        return msg;
    }

    /**
     * 发送消息给所有在线客户端
     *
     * @param msg 消息
     */
    private void sendMsgToAll(BaseMsg msg) {
        for (ConnectedUserHandler now : Server.USER_SET) {
            if (!now.socket.isClosed()) {
                try {
                    OutputStream os = now.socket.getOutputStream();
                    ObjectOutputStream oos = new ObjectOutputStream(os);
                    oos.writeObject(msg);
                    oos.flush();
                } catch (Exception e) {
                    XLogger.error(e);
                }
            }
        }
    }

    /**
     * 关闭连接，如用户没有登出则登出
     */
    @Override
    public void close() {
        if (currentUser != null && currentUser.online == 1) {
            UserDaoImpl.signOut(currentUser);
            if (userSignOutListener != null) {
                userSignOutListener.onUserSignOut();
            }
        }

        super.close();
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) {
            return true;
        }
        if (o == null || getClass() != o.getClass()) {
            return false;
        }
        ConnectedUserHandler handler = (ConnectedUserHandler) o;
        return Objects.equals(currentUser, handler.currentUser);
    }

    @Override
    public int hashCode() {
        return Objects.hashCode(currentUser);
    }

    /**
     * 用户登录监听器
     */
    public interface UserSignInListener {
        void onUserSignIn();
    }

    /**
     * 用户登出监听器
     */
    public interface UserSignOutListener {
        void onUserSignOut();
    }
}
